package com.sea.base.ext.global

import android.util.LongSparseArray
import android.util.SparseArray
import android.util.SparseIntArray
import androidx.core.util.containsKey
import kotlinx.coroutines.channels.Channel
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract

/**
 * 所有的item都返回true，则返回true
 *
 * 代替像如下方法：
 *  var allOk = true
 *  list?.forEach {
 *      if (!it.isOk) {
 *      allOk = false
 *      return@forEach
 *      }
 *  } ?: let { allOk = false }
 *
 * [all]的优化版
 *
 * @param empty 空数据返回true还是false
 * @param predicate 回调
 */
@JvmOverloads
inline fun <T> Iterable<T>?.allTrue(empty: Boolean = false, predicate: (T) -> Boolean): Boolean {
    if (this == null || (this is Collection && isEmpty())) {
        return empty
    }
    for (element in this) if (!predicate(element)) return false
    return true
}

@JvmOverloads
inline fun <T> Array<T>?.allTrue(empty: Boolean = false, predicate: (T) -> Boolean): Boolean {
    if (isNullOrEmpty()) {
        return empty
    }
    for (element in this) if (!predicate(element)) return false
    return true
}

/**
 * 有一个为true，则返回true
 *
 * @param empty 空数据返回true还是false
 * @param predicate 回调
 */
@JvmOverloads
inline fun <T> Iterable<T>?.oneTrue(empty: Boolean = false, predicate: (T) -> Boolean): Boolean {
    if (this == null || (this is Collection && isEmpty())) {
        return empty
    }
    for (element in this) if (predicate(element)) return true
    return false
}

@JvmOverloads
inline fun <T> Array<T>?.oneTrue(empty: Boolean = false, predicate: (T) -> Boolean): Boolean {
    if (isNullOrEmpty()) {
        return empty
    }
    for (element in this) if (predicate(element)) return true
    return false
}

val Collection<*>.downIndices: IntProgression
    get() = size - 1 downTo 0

/**
 * 遍历删除，倒序
 * @return 是否remove过
 */
inline fun <T> MutableList<T>?.removeIfReverseSequence(filter: (index: Int, item: T) -> Boolean): Boolean {
    var removed = false
    this?.let {
        for (index in downIndices) {
            if (filter(index, it[index])) {
                it.removeAt(index)
                removed = true
            }
        }
    }
    return removed
}

/**
 * 遍历删除，迭代器正序
 * @return 是否remove过
 */
inline fun <T> MutableCollection<T>?.removeIfIterator(filter: (item: T) -> Boolean): Boolean {
    var removed = false
    this?.let {
        val iterator = iterator()
        while (iterator.hasNext()) {
            val next = iterator.next()
            if (filter(next)) {
                iterator.remove()
                removed = true
            }
        }
    }
    return removed
}

/**
 * 只删除第一个，并返回该值
 */
inline fun <T> MutableCollection<T>?.removeFirst(filter: (item: T) -> Boolean): T? {
    this?.let {
        val iterator = iterator()
        while (iterator.hasNext()) {
            val next = iterator.next()
            if (filter(next)) {
                iterator.remove()
                return next
            }
        }
    }
    return null
}

/**
 * 根据回调判断是否包含
 * @param predicate 回调判断，true则包含此值
 */
inline fun <T> Array<out T>?.contains(predicate: (T) -> Boolean): Boolean {
    this?.forEach {
        if (predicate(it)) {
            return true
        }
    }
    return false
}

/**
 * 根据回调判断是否包含，和[oneTrue]一个逻辑，所以直接调用
 */
inline fun <T> Iterable<T>?.contains(predicate: (T) -> Boolean) = oneTrue(predicate = predicate)

/**
 * 根据回调获取对应值
 * @param predicate 回调判断，true则返回此item
 */
inline fun <T> Iterable<T>?.getOrNull(predicate: (T) -> Boolean) = this?.firstOrNull(predicate)

/**
 * 根据回调获取对应值
 * @param predicate 回调判断，true则返回此item
 */
inline fun <T> Array<T>?.getOrNull(predicate: (T) -> Boolean): T? {
    this?.forEach {
        if (predicate(it)) {
            return it
        }
    }
    return null
}

/**
 * 如果没有则put
 */
inline fun <V> SparseArray<V>.getOrPut(key: Int, defaultValue: () -> V): V {
    val value = get(key)
    return if (value == null) {
        val answer = defaultValue()
        put(key, answer)
        answer
    } else {
        value
    }
}

inline fun SparseIntArray.getOrPut(key: Int, defaultValue: () -> Int): Int {
    return if (!containsKey(key)) {
        val answer = defaultValue()
        put(key, answer)
        answer
    } else {
        get(key)
    }
}

inline fun <V> LongSparseArray<V>.getOrPut(key: Long, defaultValue: () -> V): V {
    val value = get(key)
    return if (value == null) {
        val answer = defaultValue()
        put(key, answer)
        answer
    } else {
        value
    }
}

/**
 * 倒序遍历
 */
inline fun <T> List<T>.forEachReverseSequence(action: (index: Int, T) -> Unit) {
    for (index in downIndices) {
        action(index, this[index])
    }
}

@Suppress("NOTHING_TO_INLINE")
@OptIn(ExperimentalContracts::class)
inline fun Collection<*>?.isNotNullNotEmpty(): Boolean {
    contract {
        returns(true) implies (this@isNotNullNotEmpty != null)
    }
    return !this.isNullOrEmpty()
}

@Suppress("NOTHING_TO_INLINE")
@OptIn(ExperimentalContracts::class)
inline fun Array<*>?.isNotNullNotEmpty(): Boolean {
    contract {
        returns(true) implies (this@isNotNullNotEmpty != null)
    }
    return !this.isNullOrEmpty()
}

@Suppress("NOTHING_TO_INLINE")
@OptIn(ExperimentalContracts::class)
inline fun ByteArray?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }

    return this == null || this.isEmpty()
}

@Suppress("NOTHING_TO_INLINE")
@OptIn(ExperimentalContracts::class)
inline fun CharArray?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }

    return this == null || this.isEmpty()
}

@Suppress("NOTHING_TO_INLINE")
@OptIn(ExperimentalContracts::class)
inline fun ShortArray?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }

    return this == null || this.isEmpty()
}

@Suppress("NOTHING_TO_INLINE")
@OptIn(ExperimentalContracts::class)
inline fun IntArray?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }

    return this == null || this.isEmpty()
}

@Suppress("NOTHING_TO_INLINE")
@OptIn(ExperimentalContracts::class)
inline fun LongArray?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }

    return this == null || this.isEmpty()
}

@Suppress("NOTHING_TO_INLINE")
@OptIn(ExperimentalContracts::class)
inline fun FloatArray?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }

    return this == null || this.isEmpty()
}

@Suppress("NOTHING_TO_INLINE")
@OptIn(ExperimentalContracts::class)
inline fun DoubleArray?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }

    return this == null || this.isEmpty()
}

@Suppress("NOTHING_TO_INLINE")
@OptIn(ExperimentalContracts::class)
inline fun BooleanArray?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }

    return this == null || this.isEmpty()
}

suspend inline fun <T> Channel<T>.forEach(action: (T) -> Unit) {
    for (t in this) {
        action(t)
    }
}